#!/usr/bin/env python3
"""
MCP Health Monitor - 自动化MCP服务检测工具
功能：
- 检测MCP服务状态
- 验证配置文件
- 性能监控
- 自动化健康检查
- 生成监控报告
"""

import json
import os
import sys
import time
import subprocess
import psutil
import logging
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional, Tuple
import requests
import socket

class MCPHealthMonitor:
    def __init__(self, config_path: str = "config.json"):
        self.config_path = config_path
        self.base_dir = Path(__file__).parent
        self.setup_logging()
        self.health_report = {
            "timestamp": datetime.now().isoformat(),
            "overall_status": "unknown",
            "checks": {},
            "recommendations": []
        }
    
    def setup_logging(self):
        """设置日志系统"""
        # 创建文件处理器
        file_handler = logging.FileHandler('mcp_monitor.log', encoding='utf-8')
        file_handler.setLevel(logging.INFO)
        
        # 创建控制台处理器，设置编码
        console_handler = logging.StreamHandler()
        console_handler.setLevel(logging.INFO)
        
        # 设置格式
        formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
        file_handler.setFormatter(formatter)
        console_handler.setFormatter(formatter)
        
        # 配置根日志器
        self.logger = logging.getLogger(__name__)
        self.logger.setLevel(logging.INFO)
        self.logger.addHandler(file_handler)
        self.logger.addHandler(console_handler)
        
        # 防止重复添加处理器
        self.logger.propagate = False
    
    def check_config_file(self) -> Dict:
        """检查MCP配置文件"""
        check_result = {
            "status": "fail",
            "details": {},
            "issues": []
        }
        
        try:
            config_file = self.base_dir / self.config_path
            if not config_file.exists():
                check_result["issues"].append(f"配置文件不存在: {config_file}")
                return check_result
            
            with open(config_file, 'r', encoding='utf-8') as f:
                config = json.load(f)
            
            check_result["details"]["config_loaded"] = True
            
            # 检查必要的配置项
            if "mcpServers" not in config:
                check_result["issues"].append("缺少 mcpServers 配置")
            else:
                servers = config["mcpServers"]
                check_result["details"]["server_count"] = len(servers)
                
                for server_name, server_config in servers.items():
                    # 检查命令和参数
                    if "command" not in server_config:
                        check_result["issues"].append(f"服务器 {server_name} 缺少 command 配置")
                    
                    if "args" not in server_config:
                        check_result["issues"].append(f"服务器 {server_name} 缺少 args 配置")
                    else:
                        # 检查主程序文件是否存在
                        if server_config["args"]:
                            main_file = Path(server_config["args"][0])
                            if not main_file.exists():
                                check_result["issues"].append(f"主程序文件不存在: {main_file}")
                            else:
                                check_result["details"][f"{server_name}_main_file_exists"] = True
            
            if not check_result["issues"]:
                check_result["status"] = "pass"
            
        except json.JSONDecodeError as e:
            check_result["issues"].append(f"JSON格式错误: {e}")
        except Exception as e:
            check_result["issues"].append(f"配置检查失败: {e}")
        
        return check_result
    
    def check_dependencies(self) -> Dict:
        """检查依赖项"""
        check_result = {
            "status": "fail",
            "details": {},
            "issues": []
        }
        
        try:
            # 检查Python版本
            python_version = sys.version_info
            check_result["details"]["python_version"] = f"{python_version.major}.{python_version.minor}.{python_version.micro}"
            
            if python_version < (3, 8):
                check_result["issues"].append("Python版本过低，建议使用3.8+")
            
            # 检查必要的包
            required_packages = ['fastmcp', 'numpy', 'psutil']
            missing_packages = []
            
            for package in required_packages:
                try:
                    __import__(package)
                    check_result["details"][f"{package}_installed"] = True
                except ImportError:
                    missing_packages.append(package)
                    check_result["details"][f"{package}_installed"] = False
            
            if missing_packages:
                check_result["issues"].append(f"缺少依赖包: {', '.join(missing_packages)}")
            
            # 检查requirements.txt
            req_file = self.base_dir / "requirements.txt"
            if req_file.exists():
                check_result["details"]["requirements_file_exists"] = True
            else:
                check_result["issues"].append("缺少requirements.txt文件")
            
            if not check_result["issues"]:
                check_result["status"] = "pass"
            
        except Exception as e:
            check_result["issues"].append(f"依赖检查失败: {e}")
        
        return check_result
    
    def check_process_status(self) -> Dict:
        """检查MCP进程状态"""
        check_result = {
            "status": "fail",
            "details": {},
            "issues": []
        }
        
        try:
            # 查找MCP相关进程
            mcp_processes = []
            for proc in psutil.process_iter(['pid', 'name', 'cmdline', 'cpu_percent', 'memory_info']):
                try:
                    if proc.info['cmdline'] and any('main.py' in cmd for cmd in proc.info['cmdline']):
                        mcp_processes.append({
                            'pid': proc.info['pid'],
                            'name': proc.info['name'],
                            'cmdline': ' '.join(proc.info['cmdline']),
                            'cpu_percent': proc.info['cpu_percent'],
                            'memory_mb': proc.info['memory_info'].rss / 1024 / 1024
                        })
                except (psutil.NoSuchProcess, psutil.AccessDenied):
                    continue
            
            check_result["details"]["process_count"] = len(mcp_processes)
            check_result["details"]["processes"] = mcp_processes
            
            if mcp_processes:
                check_result["status"] = "pass"
                # 检查资源使用情况
                for proc in mcp_processes:
                    if proc['memory_mb'] > 500:  # 超过500MB内存
                        check_result["issues"].append(f"进程 {proc['pid']} 内存使用过高: {proc['memory_mb']:.1f}MB")
            else:
                check_result["issues"].append("未找到运行中的MCP进程")
        
        except Exception as e:
            check_result["issues"].append(f"进程检查失败: {e}")
        
        return check_result
    
    def check_file_integrity(self) -> Dict:
        """检查文件完整性"""
        check_result = {
            "status": "fail",
            "details": {},
            "issues": []
        }
        
        try:
            # 检查关键文件
            critical_files = [
                "main.py",
                "config.json",
                "calculator_config.json",
                "README.md"
            ]
            
            for file_name in critical_files:
                file_path = self.base_dir / file_name
                if file_path.exists():
                    file_size = file_path.stat().st_size
                    check_result["details"][f"{file_name}_exists"] = True
                    check_result["details"][f"{file_name}_size"] = file_size
                    
                    # 检查文件是否为空
                    if file_size == 0:
                        check_result["issues"].append(f"文件为空: {file_name}")
                else:
                    check_result["details"][f"{file_name}_exists"] = False
                    check_result["issues"].append(f"关键文件缺失: {file_name}")
            
            # 检查日志文件
            log_files = list(self.base_dir.glob("*.log"))
            check_result["details"]["log_files_count"] = len(log_files)
            
            if not check_result["issues"]:
                check_result["status"] = "pass"
        
        except Exception as e:
            check_result["issues"].append(f"文件完整性检查失败: {e}")
        
        return check_result
    
    def test_functionality(self) -> Dict:
        """测试MCP功能"""
        check_result = {
            "status": "fail",
            "details": {},
            "issues": []
        }
        
        try:
            # 尝试导入并测试主模块
            sys.path.insert(0, str(self.base_dir))
            
            try:
                import main
                check_result["details"]["main_module_importable"] = True
                
                # 测试基本功能
                if hasattr(main, 'app'):
                    check_result["details"]["fastmcp_app_exists"] = True
                    
                    # 获取可用工具
                    if hasattr(main.app, 'list_tools'):
                        tools = main.app.list_tools()
                        check_result["details"]["available_tools"] = len(tools)
                        check_result["details"]["tool_names"] = [tool.name for tool in tools]
                    
                check_result["status"] = "pass"
                
            except ImportError as e:
                check_result["issues"].append(f"无法导入主模块: {e}")
            except Exception as e:
                check_result["issues"].append(f"模块测试失败: {e}")
        
        except Exception as e:
            check_result["issues"].append(f"功能测试失败: {e}")
        
        return check_result
    
    def check_performance(self) -> Dict:
        """性能检查"""
        check_result = {
            "status": "pass",
            "details": {},
            "issues": []
        }
        
        try:
            # 系统资源检查
            cpu_percent = psutil.cpu_percent(interval=1)
            memory = psutil.virtual_memory()
            disk = psutil.disk_usage('.')
            
            check_result["details"]["system_cpu_percent"] = cpu_percent
            check_result["details"]["system_memory_percent"] = memory.percent
            check_result["details"]["system_disk_percent"] = disk.percent
            
            # 性能警告
            if cpu_percent > 80:
                check_result["issues"].append(f"CPU使用率过高: {cpu_percent}%")
            
            if memory.percent > 85:
                check_result["issues"].append(f"内存使用率过高: {memory.percent}%")
            
            if disk.percent > 90:
                check_result["issues"].append(f"磁盘使用率过高: {disk.percent}%")
            
            # 检查日志文件大小
            log_files = list(self.base_dir.glob("*.log"))
            for log_file in log_files:
                size_mb = log_file.stat().st_size / 1024 / 1024
                if size_mb > 100:  # 超过100MB
                    check_result["issues"].append(f"日志文件过大: {log_file.name} ({size_mb:.1f}MB)")
        
        except Exception as e:
            check_result["issues"].append(f"性能检查失败: {e}")
        
        return check_result
    
    def generate_recommendations(self):
        """生成改进建议"""
        recommendations = []
        
        # 基于检查结果生成建议
        for check_name, check_result in self.health_report["checks"].items():
            if check_result["status"] == "fail":
                if check_name == "config_file":
                    recommendations.append("🔧 修复配置文件问题，确保所有必要的配置项都存在")
                elif check_name == "dependencies":
                    recommendations.append("📦 安装缺失的依赖包：pip install -r requirements.txt")
                elif check_name == "process_status":
                    recommendations.append("🚀 启动MCP服务：python main.py")
                elif check_name == "file_integrity":
                    recommendations.append("📁 检查并恢复缺失的关键文件")
                elif check_name == "functionality":
                    recommendations.append("🔍 检查代码语法错误和导入问题")
        
        # 性能优化建议
        perf_check = self.health_report["checks"].get("performance", {})
        if perf_check.get("issues"):
            recommendations.append("⚡ 优化系统性能，清理日志文件，释放系统资源")
        
        # 通用建议
        recommendations.extend([
            "📊 定期运行健康检查：python mcp_health_monitor.py",
            "📝 查看详细日志：tail -f mcp_monitor.log",
            "🔄 设置自动化监控脚本定期执行",
            "📈 监控MCP服务的性能指标和错误率",
            "🛡️ 定期备份配置文件和重要数据"
        ])
        
        self.health_report["recommendations"] = recommendations
    
    def run_all_checks(self) -> Dict:
        """运行所有检查"""
        self.logger.info("开始MCP健康检查...")
        
        checks = {
            "config_file": self.check_config_file,
            "dependencies": self.check_dependencies,
            "process_status": self.check_process_status,
            "file_integrity": self.check_file_integrity,
            "functionality": self.test_functionality,
            "performance": self.check_performance
        }
        
        passed_checks = 0
        total_checks = len(checks)
        
        for check_name, check_func in checks.items():
            self.logger.info(f"执行检查: {check_name}")
            try:
                result = check_func()
                self.health_report["checks"][check_name] = result
                
                if result["status"] == "pass":
                    passed_checks += 1
                    self.logger.info(f"[PASS] {check_name}: 通过")
                else:
                    self.logger.warning(f"[FAIL] {check_name}: 失败 - {', '.join(result['issues'])}")
            
            except Exception as e:
                self.logger.error(f"检查 {check_name} 时发生错误: {e}")
                self.health_report["checks"][check_name] = {
                    "status": "error",
                    "details": {},
                    "issues": [str(e)]
                }
        
        # 计算总体状态
        success_rate = passed_checks / total_checks
        if success_rate >= 0.8:
            self.health_report["overall_status"] = "healthy"
        elif success_rate >= 0.6:
            self.health_report["overall_status"] = "warning"
        else:
            self.health_report["overall_status"] = "critical"
        
        self.health_report["success_rate"] = f"{success_rate:.1%}"
        
        # 生成建议
        self.generate_recommendations()
        
        self.logger.info(f"健康检查完成，总体状态: {self.health_report['overall_status']}")
        return self.health_report
    
    def save_report(self, filename: str = None):
        """保存检查报告"""
        if filename is None:
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            filename = f"mcp_health_report_{timestamp}.json"
        
        report_path = self.base_dir / filename
        with open(report_path, 'w', encoding='utf-8') as f:
            json.dump(self.health_report, f, indent=2, ensure_ascii=False)
        
        self.logger.info(f"健康报告已保存: {report_path}")
        return report_path
    
    def print_summary(self):
        """打印检查摘要"""
        print("\n" + "="*60)
        print("🏥 MCP健康检查报告")
        print("="*60)
        print(f"⏰ 检查时间: {self.health_report['timestamp']}")
        print(f"📊 总体状态: {self.health_report['overall_status'].upper()}")
        print(f"✅ 成功率: {self.health_report['success_rate']}")
        
        print("\n📋 检查详情:")
        for check_name, result in self.health_report["checks"].items():
            status_icon = "✅" if result["status"] == "pass" else "❌"
            print(f"  {status_icon} {check_name}: {result['status']}")
            
            if result["issues"]:
                for issue in result["issues"]:
                    print(f"    ⚠️  {issue}")
        
        print("\n💡 改进建议:")
        for i, rec in enumerate(self.health_report["recommendations"][:5], 1):
            print(f"  {i}. {rec}")
        
        print("\n" + "="*60)

def main():
    """主函数"""
    monitor = MCPHealthMonitor()
    
    # 运行健康检查
    report = monitor.run_all_checks()
    
    # 打印摘要
    monitor.print_summary()
    
    # 保存报告
    report_file = monitor.save_report()
    
    # 根据状态设置退出码
    if report["overall_status"] == "healthy":
        sys.exit(0)
    elif report["overall_status"] == "warning":
        sys.exit(1)
    else:
        sys.exit(2)

if __name__ == "__main__":
    main()